/* =======================================================================
 * jQuery.raty.js v2.4.5- A Star Rating Plugin
 * -------------------------------------------------------------------
 * jQuery Raty is a plugin that generates a customizable star rating.
 * Licensed under The MIT License
 *
 * @version        2.4.5
 * @since          2010.06.11
 * @author         Washington Botelho
 * @documentation  wbotelhos.com/raty
 * @twitter        twitter.com/wbotelhos
 *
 * Usage:
 * -------------------------------------------------------------------
 * $('#star').raty();
 * <div id="star"></div>
 * ======================================================================== */
!(function($) {
  var methods = {
    init: function(settings) {
      return this.each(function() {
        var self = this,
        $this = $(self).empty();
        self.opt = $.extend(true, {},
        $.fn.raty.defaults, settings);
        $this.data('settings', self.opt);
        self.opt.number = methods.between(self.opt.number, 0, 20);
        if (self.opt.path.substring(self.opt.path.length - 1, self.opt.path.length) != '/') {
          self.opt.path += '/';
        }
        if (typeof self.opt.score == 'function') {
          self.opt.score = self.opt.score.call(self);
        }
        if (self.opt.score) {
          self.opt.score = methods.between(self.opt.score, 0, self.opt.number);
        }
        for (var i = 1; i <= self.opt.number; i++) {
          $('<img />', {
            src: self.opt.path + ((!self.opt.score || self.opt.score < i) ? self.opt.starOff: self.opt.starOn),
            alt: i,
            title: (i <= self.opt.hints.length && self.opt.hints[i - 1] !== null) ? self.opt.hints[i - 1] : i
          }).appendTo(self);

          if (self.opt.space) {
            $this.append((i < self.opt.number) ? '': '');
          }
        }

        self.stars = $this.children('img:not(".raty-cancel")');
        self.score = $('<input />', {
          type: 'hidden',
          name: self.opt.scoreName
        }).appendTo(self);
        if (self.opt.score && self.opt.score > 0) {
          self.score.val(self.opt.score);
          methods.roundStar.call(self, self.opt.score);
        }
        if (self.opt.iconRange) {
          methods.fill.call(self, self.opt.score);        }
        methods.setTarget.call(self, self.opt.score, self.opt.targetKeep);
        var space = self.opt.space ? 4 : 0,
        width = self.opt.width || (self.opt.number * self.opt.size + self.opt.number * space);
        if (self.opt.cancel) {
          self.cancel = $('<img />', {
            src: self.opt.path + self.opt.cancelOff,
            alt: 'x',
            title: self.opt.cancelHint,
            'class': 'raty-cancel'
          });
          if (self.opt.cancelPlace == 'left') {
            //$this.prepend('&#160;').prepend(self.cancel);
          } else {
            //$this.append('&#160;').append(self.cancel);
          }
          width += (self.opt.size + space);
        }
        if (self.opt.readOnly) {
          methods.fixHint.call(self);

          if (self.cancel) {
            self.cancel.hide();
          }
        } else {
          $this.css('cursor', 'pointer');
          methods.bindAction.call(self);
        }
        //$this.css('width', width);
      });
    },
    between: function(value, min, max) {
      return Math.min(Math.max(parseFloat(value), min), max);
    },
    bindAction: function() {
      var self = this,
      $this = $(self);
      $this.mouseleave(function() {
        var score = self.score.val() || undefined;

        methods.initialize.call(self, score);
        methods.setTarget.call(self, score, self.opt.targetKeep);

        if (self.opt.mouseover) {
          self.opt.mouseover.call(self, score);
        }
      });
      var action = self.opt.half ? 'mousemove': 'mouseover';
      if (self.opt.cancel) {
        self.cancel.mouseenter(function() {
          $(this).attr('src', self.opt.path + self.opt.cancelOn);

          self.stars.attr('src', self.opt.path + self.opt.starOff);

          methods.setTarget.call(self, null, true);

          if (self.opt.mouseover) {
            self.opt.mouseover.call(self, null);
          }
        }).mouseleave(function() {
          $(this).attr('src', self.opt.path + self.opt.cancelOff);

          if (self.opt.mouseover) {
            self.opt.mouseover.call(self, self.score.val() || null);
          }
        }).click(function(evt) {
          self.score.removeAttr('value');
          if (self.opt.click) {
            self.opt.click.call(self, null, evt);
          }
        });
      }

      self.stars.bind(action,
      function(evt) {
        var value = parseInt(this.alt, 10);
        if (self.opt.half) {
          var position = parseFloat((evt.pageX - $(this).offset().left) / self.opt.size),
          diff = (position > .5) ? 1 : .5;
          value = parseFloat(this.alt) - 1 + diff;
          methods.fill.call(self, value);
          if (self.opt.precision) {
            value = value - diff + position;
          }
          methods.showHalf.call(self, value);
        } else {
          methods.fill.call(self, value);
        }
        $this.data('score', value);
        methods.setTarget.call(self, value, true);
        if (self.opt.mouseover) {
          self.opt.mouseover.call(self, value, evt);
        }
      }).click(function(evt) {
        self.score.val((self.opt.half || self.opt.precision) ? $this.data('score') : this.alt);
        if (self.opt.click) {
          self.opt.click.call(self, self.score.val(), evt);
        }
      });
    },
    cancel: function(isClick) {
      return $(this).each(function() {
        var self = this,
        $this = $(self);

        if ($this.data('readonly') === true) {
          return this;
        }

        if (isClick) {
          methods.click.call(self, null);
        } else {
          methods.score.call(self, null);
        }

        self.score.removeAttr('value');
      });
    },
    click: function(score) {
      return $(this).each(function() {
        if ($(this).data('readonly') === true) {
          return this;
        }

        methods.initialize.call(this, score);

        if (this.opt.click) {
          this.opt.click.call(this, score);
        } else {
          methods.error.call(this, 'you must add the "click: function(score, evt) { }" callback.');
        }
        methods.setTarget.call(this, score, true);
      });
    },
    error: function(message) {
      $(this).html(message);
      $.error(message);
    },
    fill: function(score) {
      var self = this,
      number = self.stars.length,
      count = 0,
      $star, star, icon;
      for (var i = 1; i <= number; i++) {
        $star = self.stars.eq(i - 1);
        if (self.opt.iconRange && self.opt.iconRange.length > count) {
          star = self.opt.iconRange[count];
          if (self.opt.single) {
            icon = (i == score) ? (star.on || self.opt.starOn) : (star.off || self.opt.starOff);
          } else {
            icon = (i <= score) ? (star.on || self.opt.starOn) : (star.off || self.opt.starOff);
          }
          if (i <= star.range) {
            $star.attr('src', self.opt.path + icon);
          }
          if (i == star.range) {
            count++;
          }
        } else {
          if (self.opt.single) {
            icon = (i == score) ? self.opt.starOn: self.opt.starOff;
          } else {
            icon = (i <= score) ? self.opt.starOn: self.opt.starOff;
          }
          $star.attr('src', self.opt.path + icon);
        }
      }
    },
    fixHint: function() {
      var $this = $(this),
      score = parseInt(this.score.val(), 10),
      hint = this.opt.noRatedMsg;

      if (!isNaN(score) && score > 0) {
        hint = (score <= this.opt.hints.length && this.opt.hints[score - 1] !== null) ? this.opt.hints[score - 1] : score;
      }
      $this.data('readonly', true).css('cursor', 'default').attr('title', hint);
      this.score.attr('readonly', 'readonly');
      this.stars.attr('title', hint);
    },
    getScore: function() {
      var score = [],
      value;
      $(this).each(function() {
        value = this.score.val();
        score.push(value ? parseFloat(value) : undefined);
      });
      return (score.length > 1) ? score: score[0];
    },
    readOnly: function(isReadOnly) {
      return this.each(function() {
        var $this = $(this);
        if ($this.data('readonly') === isReadOnly) {
          return this;
        }
        if (this.cancel) {
          if (isReadOnly) {
            this.cancel.hide();
          } else {
            this.cancel.show();
          }
        }
        if (isReadOnly) {
          $this.unbind();
          $this.children('img').unbind();
          methods.fixHint.call(this);
        } else {
          methods.bindAction.call(this);
          methods.unfixHint.call(this);
        }

        $this.data('readonly', isReadOnly);
      });
    },
    reload: function() {
      return methods.set.call(this, {});
    },
    roundStar: function(score) {
      var diff = (score - Math.floor(score)).toFixed(2);

      if (diff > this.opt.round.down) {
        var icon = this.opt.starOn; // Full up: [x.76 .. x.99]
        if (diff < this.opt.round.up && this.opt.halfShow) { // Half: [x.26 .. x.75]
          icon = this.opt.starHalf;
        } else if (diff < this.opt.round.full) { // Full down: [x.00 .. x.5]
          icon = this.opt.starOff;
        }

        this.stars.eq(Math.ceil(score) - 1).attr('src', this.opt.path + icon);
      } // Full down: [x.00 .. x.25]
    },
    score: function() {
      return arguments.length ? methods.setScore.apply(this, arguments) : methods.getScore.call(this);
    },
    set: function(settings) {
      this.each(function() {
        var $this = $(this),
        actual = $this.data('settings'),
        clone = $this.clone().removeAttr('style').insertBefore($this);

        $this.remove();

        clone.raty($.extend(actual, settings));
      });

      return $(this.selector);
    },
    setScore: function(score) {
      return $(this).each(function() {
        if ($(this).data('readonly') === true) {
          return this;
        }

        methods.initialize.call(this, score);
        methods.setTarget.call(this, score, true);
      });
    },
    setTarget: function(value, isKeep) {
      if (this.opt.target) {
        var $target = $(this.opt.target);

        if ($target.length == 0) {
          methods.error.call(this, '目标选择器无效或丢失!');
        }

        var score = value;

        if (!isKeep || score === undefined) {
          score = this.opt.targetText;
        } else {
          if (this.opt.targetType == 'hint') {
            score = (score === null && this.opt.cancel) ? this.opt.cancelHint: this.opt.hints[Math.ceil(score - 1)];
          } else {
            score = this.opt.precision ? parseFloat(score).toFixed(1) : parseInt(score, 10);
          }
        }

        if (this.opt.targetFormat.indexOf('{score}') < 0) {
          methods.error.call(this, '模版 "{score}" 找不到!');
        }

        if (value !== null) {
          score = this.opt.targetFormat.toString().replace('{score}', score);
        }

        if ($target.is(':input')) {
          $target.val(score);
        } else {
          $target.html(score);
        }
      }
    },
    showHalf: function(score) {
      var diff = (score - Math.floor(score)).toFixed(1);

      if (diff > 0 && diff < .6) {
        this.stars.eq(Math.ceil(score) - 1).attr('src', this.opt.path + this.opt.starHalf);
      }
    },
    initialize: function(score) {
      score = !score ? 0 : methods.between(score, 0, this.opt.number);

      methods.fill.call(this, score);

      if (score > 0) {
        if (this.opt.halfShow) {
          methods.roundStar.call(this, score);
        }

        this.score.val(score);
      }
    },
    unfixHint: function() {
      for (var i = 0; i < this.opt.number; i++) {
        this.stars.eq(i).attr('title', (i < this.opt.hints.length && this.opt.hints[i] !== null) ? this.opt.hints[i] : i);
      }

      $(this).data('readonly', false).css('cursor', 'pointer').removeAttr('title');

      this.score.attr('readonly', 'readonly');
    }
  };

  $.fn.raty = function(method) {
    if (methods[method]) {
      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    } else if (typeof method === 'object' || !method) {
      return methods.init.apply(this, arguments);
    } else {
      $.error('方法 ' + method + ' 不存在!');
    }
  };

  $.fn.raty.defaults = {
    cancel: false,
    cancelHint: '取消评级!',
    cancelOff: 'cancel-off.png',
    cancelOn: 'cancel-on.png',
    cancelPlace: 'left',
    click: undefined,
    half: false,
    halfShow: true,
    hints: ['10', '20', '30', '40', '50', '60', '70', '80', '90', '100'],
    iconRange: undefined,
    mouseover: undefined,
    noRatedMsg: '没有额定',
    number: 10,
    path: 'images/',
    precision: false,
    round: {
      down: .25,
      full: .6,
      up: .76
    },
    readOnly: false,
    score: undefined,
    scoreName: 'score',
    single: false,
    size: 16,
    space: true,
    starHalf: 'star-half.png',
    starOff: 'star-off.png',
    starOn: 'star-on.png',
    target: undefined,
    targetFormat: '{score}',
    targetKeep: false,
    targetText: '',
    targetType: 'hint',
    width: undefined
  };
})(jQuery);